package core;

import java.nio.ByteBuffer;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import interfaces.*;
import proxy.*;
import util.ExecutionTime;
import util.FileLog;

public class Worker extends Thread implements IWorker, IRemoteNode{

	// Define o procurador do trabalhador
	private WorkerProxy workerProxy;
	
	// Define o canal de comunicacao do worker
	private ICommunicator workerChannel;
	
	// Atualizador de estado do trabalhador
	UpdateStatusWorker updateStatusWorker;
	
	// Define uma fila de trabalhos para o trabalhador
	private BlockingDeque<IJob> jobsList;
	
	// Cria um arquivo de log para os tempos de execucao
    // Armazena o JobId + o tempo de execucao
    FileLog timeExecLog;
    
    // Calcula o tempo de execucao
 	ExecutionTime executionTime;
 	
 	// Define um trabalho para o trabalhador
 	IJob job;
	
	// Define a identificacao do trabalhador
	private String workerID;
	
	// Define a capacidade do trabalhador
	private float workerCapacity;
	
	// define a carga de trabalho do trabalhador
	private int workerWorkload;
	
	// Define o contato com o chefe deste trabalhador
	private String workerBoss;
	
	// Define a descricao do canal do trabalhador
	private String workerChannelDesc;
	
	// Define o tamanho de trabalho do trabalhador
	private int workSize;
	
	// Define o tempo de execucao de uma tarefa
	private float executionTimeJob;
	
	public Worker() {
		
	}

	public Worker(String id, WorkerProxy workerProxy) {
		try {
			
			this.workerID = id;
			
			this.workerProxy = workerProxy;
			
			this.setChannel(this.workerProxy.getChannel());
			
			this.setChannelDesc(this.workerProxy.getChannelDesc());
			
			this.jobsList = new LinkedBlockingDeque<IJob>();
			 
			this.timeExecLog = new FileLog("ExecutionTime", this.getID());
				
			this.updateStatusWorker = new UpdateStatusWorker(this);
				
			this.executionTime = new ExecutionTime();
							
			this.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void run() {
		try {
			while (true) {
				this.job = this.jobsList.take();
				
				////////////////////////////////////////////////////////////////////////
				System.out.println("Worker: " + this.getID());
				System.out.println("\t Vou resolver o JOB: " + job.getID());
				System.out.println("\t\t  TAMANHO: " + job.getWorkload() + "\n");
				////////////////////////////////////////////////////////////////////////

				// Inicia a contagem de execucao de uma tarefa
				this.executionTime.start();

				// Resolve o job
				this.job.solve();

				// encerra o tempo de execucao
				this.executionTime.stop();

				// processa o tempo em ms
				this.executionTimeJob = this.executionTime.getTime();
				
				if (this.executionTimeJob <= 0) {
					this.executionTimeJob = 1;
				}
				
				// Contabiliza o WorkerID + JobID + jobWorkload + JobResult +
                // TempoDeExec em um arquivo (logs/ExecutionTime*)
                ByteBuffer result = ByteBuffer.wrap(job.getResultDescription());
                this.timeExecLog.print(this.getID() + "\t\t" + this.job.getID() + "\t\t" + "\t\t" + this.job.getNumero() + 
                		"\t\t" + result.getLong() + "\t\t"+ this.executionTime.stime());
	
				this.workerCapacity = (this.job.getWorkload() / this.executionTimeJob);
				
				// Envia o resultado para o seu gerente
				this.workerProxy.resultSend(this.getID(), this.job.getID(), this.job.getResultDescription());
				
				////////////////////////////////////////////////////////////////////////
				System.out.println("Worker: " + this.getID());
				System.out.println("\t Resolvi o JOB: " + this.job.getID() + "\n");
				//System.out.println("\t\t Levou (s): " + this.executionTime.stime());
				//System.out.println("\t\t Capacidade: " + this.getCapacity());
				//System.out.println("\t\t Carga: " + this.getWorkload() + "\n");
				// //////////////////////////////////////////////////////////////////////

				this.reduceWorkerWorkload(this.job.getWorkload());
				
				// Notifica a Thread updateStatusWorker para enviar o status deste Worker
				synchronized (this.updateStatusWorker) {
					this.updateStatusWorker.notifyAll();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void bossContact(String workerId) {
		// TODO Auto-generated method stub
		try {
			/////////////////////////////////////////////////////////////////////////
			//System.out.println("Worker: " + this.getID() + " =>");
			//System.out.println("\t Recebi Msg. BOSS_CONTACT");
			//System.out.println("\t\t Meu chefe eh: " + workerId +"\n");
			/////////////////////////////////////////////////////////////////////////
			this.setBossContact(workerId);
			this.workerProxy.managerConnect(this.getBossContact());
			this.setCapacity((float)0.5);
			this.updateStatusWorker.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void newJob(String jobId, byte[] jobDescription) {
		// TODO Auto-generated method stub
		try {
			/////////////////////////////////////////////////////////////////////////
			//System.out.println("Worker: " + this.getID() + " =>");
			//System.out.println("\t Recebi Msg. NEW_JOB \n");
			/////////////////////////////////////////////////////////////////////////
			IJob job = new Fibo(jobDescription);
			job.setID(jobId);
			this.increaseWorkerWorkload(job.getWorkload());
			this.jobsList.put(job);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public String getBossContact() {
		// TODO Auto-generated method stub
		return this.workerBoss;
	}

	@Override
	public float getCapacity() {
		// TODO Auto-generated method stub
		return this.workerCapacity;
	}

	@Override
	public ICommunicator getChannel() {
		// TODO Auto-generated method stub
		return this.workerChannel;
	}

	@Override
	public String getChannelDesc() {
		// TODO Auto-generated method stub
		return this.workerChannelDesc;
	}

	@Override
	public String getID() {
		// TODO Auto-generated method stub
		return this.workerID;
	}

	@Override
	public int getRequestSize() {
		// TODO Auto-generated method stub
		return this.workSize;
	}

	@Override
	public int getWorkload() {
		// TODO Auto-generated method stub
		return this.workerWorkload;
	}

	@Override
	public void setBossContact(String workerId) {
		// TODO Auto-generated method stub
		this.workerBoss = workerId;
	}

	@Override
	public void setCapacity(float capacity) {
		// TODO Auto-generated method stub
		this.workerCapacity = capacity;
	}

	@Override
	public void setChannel(ICommunicator channel) {
		// TODO Auto-generated method stub
		this.workerChannel = channel;
	}

	@Override
	public void setChannelDesc(String channelDesc) {
		// TODO Auto-generated method stub
		this.workerChannelDesc = channelDesc;
	}

	@Override
	public void setID(String id) {
		// TODO Auto-generated method stub
		this.workerID = id;
	}

	@Override
	public void setRequestSize(int requestSize) {
		// TODO Auto-generated method stub
		this.workSize = requestSize;
	}

	@Override
	public void setWorkload(int workload) {
		// TODO Auto-generated method stub
		this.workerWorkload = workload;
	}
	
	public void reduceWorkerWorkload(int workload) {
		try {
			this.workerWorkload -= workload;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void increaseWorkerWorkload(int workload) {
		try {
			this.workerWorkload += workload;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void statusSend() {
		try {
			this.workerProxy.statusSend(this.getID(), this.getCapacity(), this.getWorkload(), System.currentTimeMillis());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
